package cn.aitrox.ry.service.anno.service.impl;

import cn.aitrox.ry.bean.RespResult;
import cn.aitrox.ry.common.interceptor.UserThreadLocal;
import cn.aitrox.ry.common.service.BaseService;
import cn.aitrox.ry.enumtype.RespCodeEnum;
import cn.aitrox.ry.service.anno.dao.AnnoLesionResultDao;
import cn.aitrox.ry.service.anno.dao.AnnoLesionResultDetailDao;
import cn.aitrox.ry.service.anno.dao.AnnoLesionToolDao;
import cn.aitrox.ry.service.anno.dao.AnnoResultDao;
import cn.aitrox.ry.service.anno.dto.anno.ComponentDto;
import cn.aitrox.ry.service.anno.dto.anno.LesionDto;
import cn.aitrox.ry.service.anno.dto.anno.lesion.result.in.AlrMaskCreateInDto;
import cn.aitrox.ry.service.anno.dto.anno.lesion.result.in.AlrMaskUpdateInDto;
import cn.aitrox.ry.service.anno.dto.anno.lesion.result.in.AlrPointCreateInDto;
import cn.aitrox.ry.service.anno.dto.anno.lesion.result.in.MaskLayerResultDto;
import cn.aitrox.ry.service.anno.dto.anno.setting.out.AnnoSettingInfoDto;
import cn.aitrox.ry.service.anno.entity.AnnoLesionResultDetailEntity;
import cn.aitrox.ry.service.anno.entity.AnnoLesionResultEntity;
import cn.aitrox.ry.service.anno.entity.AnnoLesionToolEntity;
import cn.aitrox.ry.service.anno.entity.AnnoResultEntity;
import cn.aitrox.ry.service.anno.enumtype.anno.LesionResultTypeEnum;
import cn.aitrox.ry.service.anno.service.AnnoLesionResultService;
import cn.aitrox.ry.service.anno.service.AnnoResultService;
import cn.aitrox.ry.service.anno.service.AnnoSettingService;
import cn.aitrox.ry.util.ArrayUtil;
import cn.aitrox.ry.util.JSONUtil;
import cn.aitrox.ry.util.UUIDUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class AnnoLesionResultServiceImpl extends BaseService implements AnnoLesionResultService {

    @Autowired
    private AnnoLesionResultDao annoLesionResultDao;

    @Autowired
    private AnnoResultDao annoResultDao;

    @Autowired
    private AnnoLesionToolDao annoLesionToolDao;

    @Autowired
    private UserThreadLocal userThreadLocal;

    @Autowired
    private AnnoResultService annoResultService;

    @Autowired
    private AnnoLesionResultDetailDao annoLesionResultDetailDao;

    @Autowired
    private AnnoSettingService annoSettingService;

    @Transactional
    @Override
    public RespResult<Integer> pointCreate(AlrPointCreateInDto input) {
        // 校验该标注结果是否属于该用户
        RespResult<AnnoResultEntity> checkDataUserResult = annoResultService.checkAr(userThreadLocal.getUser().getId(), input.getArId());
        if (!checkDataUserResult._isOk()) {
            return RespResult.secByError(checkDataUserResult.getCode(), checkDataUserResult.getMsg());
        }

        Integer taskId = checkDataUserResult.getData().getTaskId();
        // 校验病灶和工具是否有设置
        RespResult<AnnoLesionResultEntity> checkLesionToolValueResult = this.checkLesionToolValue(taskId, input.getLesion(), input.getTool());
        if (!checkDataUserResult._isOk()) {
            return RespResult.secByError(checkLesionToolValueResult.getCode(), checkLesionToolValueResult.getMsg());
        }

        AnnoLesionResultEntity alr = new AnnoLesionResultEntity(null, taskId, input.getArId(), input.getLesion(), input.getTool(), input.getResult(), input.getColor(), LesionResultTypeEnum.POINT.getType(), null, null, input.getGroupId(), false);
        annoLesionResultDao.insertSelective(alr);
        return RespResult.ok(alr.getId());
    }

    @Transactional
    @Override
    public RespResult pointUpdate(Integer alrId, String result, String color) {
        RespResult<AnnoLesionResultEntity> checkDataUserResult = this.checkALr(userThreadLocal.getUser().getId(), alrId);
        if (!checkDataUserResult._isOk()) {
            return RespResult.secByError(checkDataUserResult.getCode(), checkDataUserResult.getMsg());
        }
        AnnoLesionResultEntity update = new AnnoLesionResultEntity(alrId, null, null, null, null, result, color, null, null, null, null, null);
        annoLesionResultDao.updateByIdSelective(update);

        return RespResult.ok();
    }

    @Transactional
    @Override
    public RespResult<Integer> maskCreate(AlrMaskCreateInDto input) {
        // 校验该标注结果是否属于该用户
        RespResult<AnnoResultEntity> checkDataUserResult = annoResultService.checkAr(userThreadLocal.getUser().getId(), input.getArId());
        if (!checkDataUserResult._isOk()) {
            return RespResult.secByError(checkDataUserResult.getCode(), checkDataUserResult.getMsg());
        }

        Integer taskId = checkDataUserResult.getData().getTaskId();
        // 校验病灶和工具是否有设置
        RespResult<AnnoLesionResultEntity> checkLesionToolValueResult = this.checkLesionToolValue(taskId, input.getLesion(), input.getTool());
        if (!checkDataUserResult._isOk()) {
            return RespResult.secByError(checkLesionToolValueResult.getCode(), checkLesionToolValueResult.getMsg());
        }

        String maskResultUid = UUIDUtil.getUUID();
        AnnoLesionResultEntity alr = new AnnoLesionResultEntity(null, taskId, input.getArId(), input.getLesion(), input.getTool(), maskResultUid, null, LesionResultTypeEnum.MASK.getType(), null, null, input.getGroupId(), false);
        annoLesionResultDao.insertSelective(alr);

        String resultJson = JSONUtil.toJson(input.getResult());
        List<AnnoLesionResultDetailEntity> detailEntityList = new ArrayList<>();
        for (int i = input.getLayerStart(); i <= input.getLayerEnd(); i++) {
            AnnoLesionResultDetailEntity detailEntity = new AnnoLesionResultDetailEntity(null, alr.getId(), maskResultUid, null, null, i, resultJson, LesionResultTypeEnum.MASK.getType(), "V1");
            detailEntityList.add(detailEntity);
        }

        annoLesionResultDetailDao.batchSave(detailEntityList);

        return RespResult.ok(alr.getId());
    }

    @Transactional
    @Override
    public RespResult maskUpdate(AlrMaskUpdateInDto input) {
        RespResult<AnnoLesionResultEntity> checkDataUserResult = this.checkALr(userThreadLocal.getUser().getId(), input.getAlrId());
        if (!checkDataUserResult._isOk()) {
            return RespResult.secByError(checkDataUserResult.getCode(), checkDataUserResult.getMsg());
        }

        AnnoLesionResultEntity alr = checkDataUserResult.getData();

        annoLesionResultDetailDao.deleteByAlrIdGroupId(alr.getId(), alr.getResult());

        List<AnnoLesionResultDetailEntity> detailEntityList = new ArrayList<>();
        for (MaskLayerResultDto maskLayerResult : input.getLayerResultList()) {
            String resultJson = JSONUtil.toJson(maskLayerResult.getResult());
            AnnoLesionResultDetailEntity detailEntity = new AnnoLesionResultDetailEntity(null, alr.getId(), alr.getResult(), null, null, maskLayerResult.getLayer(), resultJson, LesionResultTypeEnum.MASK.getType(), "V1");
            detailEntityList.add(detailEntity);
        }

        annoLesionResultDetailDao.batchSave(detailEntityList);
        return RespResult.ok();
    }

    @Transactional
    @Override
    public RespResult lesionUpdate(Integer alrId, String lesionValue) {
        RespResult<AnnoLesionResultEntity> checkDataUserResult = this.checkALr(userThreadLocal.getUser().getId(), alrId);
        if (!checkDataUserResult._isOk()) {
            return RespResult.secByError(checkDataUserResult.getCode(), checkDataUserResult.getMsg());
        }
        AnnoLesionResultEntity alr = checkDataUserResult.getData();

        // 校验任务是否配置过该病灶
        RespResult<AnnoSettingInfoDto> annoSettingReadResult = annoSettingService.read(alr.getTaskId());
        if (!annoSettingReadResult._isOk()) {
            return RespResult.secByError(annoSettingReadResult.getCode(), annoSettingReadResult.getMsg());
        }

        AnnoSettingInfoDto annoSettingInfo = annoSettingReadResult.getData();

        LesionDto newLesionConfig = annoSettingInfo.getLesionList().stream().filter(lesion -> lesion.getLesion().equals(lesionValue)).findFirst().orElse(null);
        if (null == newLesionConfig) {
            return RespResult.secByError(RespCodeEnum.NOT_EXIST.getCode(), "未配置该病灶");
        }

        if (!newLesionConfig.getToolList().contains(alr.getTool())) {
            return RespResult.secByError(RespCodeEnum.NOT_EXIST.getCode(), "病灶工具不匹配");
        }

        LesionDto oldLesionConfig = annoSettingInfo.getLesionList().stream().filter(lesion -> lesion.getLesion().equals(alr.getLesion())).findFirst().orElse(null);

        // 校验小征象是否与原病灶匹配
        if (!ArrayUtil.isEmpty(oldLesionConfig.getImageCompList())) {
            List<Integer> oldLesionCompIds = oldLesionConfig.getImageCompList().stream().map(ComponentDto::getComponentId).collect(Collectors.toList());
            if (ArrayUtil.isEmpty(newLesionConfig.getImageCompList())) {
                return RespResult.secByError(RespCodeEnum.NOT_EXIST.getCode(), "病灶小征象不匹配");
            }
            if (newLesionConfig.getImageCompList().stream().filter(x -> !oldLesionCompIds.contains(x.getComponentId())).count() > 0) {
                return RespResult.secByError(RespCodeEnum.NOT_EXIST.getCode(), "病灶小征象不匹配");
            }
        }

        AnnoLesionResultEntity alrUpdate = new AnnoLesionResultEntity();
        alrUpdate.setId(alr.getId());
        alrUpdate.setLesion(lesionValue);
        annoLesionResultDao.updateByIdSelective(alrUpdate);

        return RespResult.ok();
    }

    @Transactional
    @Override
    public RespResult delete(Integer alrId) {
        // TODO 暂时只考虑删除点位数据，mask之后再完善
        RespResult<AnnoLesionResultEntity> checkDataUserResult = this.checkALr(userThreadLocal.getUser().getId(), alrId);
        if (!checkDataUserResult._isOk()) {
            return RespResult.secByError(checkDataUserResult.getCode(), checkDataUserResult.getMsg());
        }
        annoLesionResultDao.deleteById(alrId);
        return RespResult.ok();
    }

    private RespResult<AnnoLesionResultEntity> checkLesionToolValue(Integer taskId, String lesion, String tool) {
        AnnoLesionToolEntity annoLesionToolEntity = annoLesionToolDao.findByTaskIdLesionTool(taskId, lesion, tool);
        if (null == annoLesionToolEntity) {
            return RespResult.error(RespCodeEnum.NOT_EXIST);
        }
        return RespResult.ok(annoLesionToolEntity);
    }

    private RespResult<AnnoLesionResultEntity> checkALr(Integer userId, Integer alrId) {
        AnnoLesionResultEntity alr = annoLesionResultDao.findById(alrId);
        if (null == alr) {
            return RespResult.error(RespCodeEnum.NOT_EXIST);
        }
        AnnoResultEntity annoResult = annoResultDao.findById(alr.getArId());
        if (null == annoResult) {
            return RespResult.error(RespCodeEnum.SYS_DATA_ERROR);
        }
        if (!annoResult.getUserId().equals(userId)) {
            return RespResult.error(RespCodeEnum.NO_PERMISSION);
        }
        return RespResult.ok(alr);
    }
}
